package com.abel.bigwater.wflow

import org.activiti.api.process.model.builders.ProcessPayloadBuilder
import org.activiti.api.process.runtime.ProcessRuntime
import org.activiti.api.process.runtime.connector.Connector
import org.activiti.api.runtime.shared.query.Pageable
import org.apache.commons.io.FileUtils
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.Bean
import org.springframework.integration.annotation.InboundChannelAdapter
import org.springframework.integration.annotation.Poller
import org.springframework.integration.annotation.ServiceActivator
import org.springframework.integration.channel.DirectChannel
import org.springframework.integration.config.EnableIntegration
import org.springframework.integration.core.MessageSource
import org.springframework.integration.file.FileReadingMessageSource
import org.springframework.integration.file.filters.SimplePatternFileListFilter
import org.springframework.messaging.Message
import org.springframework.messaging.MessageChannel
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*

@SpringBootApplication
@EnableIntegration
class BwWflowApplication(private val processRuntime: ProcessRuntime,
                         private val securityUtil: SecurityUtil) : CommandLineRunner {

    @Value("\${server.port}")
    var serverPort: String? = null

    override fun run(vararg args: String) {
//        securityUtil.logInAs("system")
        logger.info("服务端口: $serverPort")
        securityUtil.logInAs("admin")

        val processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10))
        logger.info("> Available Process definitions: " + processDefinitionPage.totalItems)
        for (pd in processDefinitionPage.content) {
            logger.info("\t > Process definition: $pd")
        }

    }

    @Bean
    fun fileChannel(): MessageChannel {
        return DirectChannel()
    }

    @Bean
    @InboundChannelAdapter(value = "fileChannel", poller = [Poller(fixedDelay = "1000")])
    fun fileReadingMessageSource(): MessageSource<File> {
        val sourceReader = FileReadingMessageSource()
        sourceReader.setDirectory(File(INPUT_DIR))
        sourceReader.setFilter(SimplePatternFileListFilter(FILE_PATTERN))
        return sourceReader
    }

    @ServiceActivator(inputChannel = "fileChannel")
    @Throws(IOException::class)
    fun processFile(message: Message<File>) {
        securityUtil.logInAs("system")

        val payload = message.payload
        logger.info(">>> Processing file: " + payload.name)

        val content = FileUtils.readFileToString(payload, "UTF-8")

        val formatter = SimpleDateFormat("dd-MM-yy HH:mm:ss")

        logger.info("> Processing content: " + content + " at " + formatter.format(Date()))

        val processInstance = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("categorizeProcess")
                .withName("Processing Content: $content")
                .withVariable("content", content)
                .build())
        logger.info(">>> Created Process Instance: $processInstance")

        logger.info(">>> Deleting processed file: " + payload.name)
        payload.delete()

    }


    @Bean
    fun processTextConnector(): Connector {
        return Connector { integrationContext ->
            val inBoundVariables = integrationContext.getInBoundVariables()
            val contentToProcess = inBoundVariables.get("content") as String
            // Logic Here to decide if content is approved or not
            if (contentToProcess.contains("activiti")) {
                logger.info("> Approving content: $contentToProcess")
                integrationContext.addOutBoundVariable("approved",
                        true)
            } else {
                logger.info("> Discarding content: $contentToProcess")
                integrationContext.addOutBoundVariable("approved",
                        false)
            }
            integrationContext
        }
    }

    @Bean
    fun tagTextConnector(): Connector {
        return Connector { integrationContext ->
            var contentToTag = integrationContext.getInBoundVariables().get("content") as String
            contentToTag += " :) "
            integrationContext.addOutBoundVariable("content",
                    contentToTag)
            logger.info("Final Content: $contentToTag")
            integrationContext
        }
    }

    @Bean
    fun discardTextConnector(): Connector {
        return Connector { integrationContext ->
            var contentToDiscard = integrationContext.getInBoundVariables().get("content") as String
            contentToDiscard += " :( "
            integrationContext.addOutBoundVariable("content",
                    contentToDiscard)
            logger.info("Final Content: $contentToDiscard")
            integrationContext
        }
    }

    companion object {

        private val INPUT_DIR = "/tmp/"
        private val FILE_PATTERN = "*.txt"
        private val logger = LoggerFactory.getLogger(BwWflowApplication::class.java)

        const val VERSION_STR = "bw-wflow (v20190901.1)"

        @JvmStatic
        fun main(args: Array<String>) {
            logger.info("工单系统 ${VERSION_STR} 启动中...")

            SpringApplication.run(BwWflowApplication::class.java, *args)
        }
    }

}